package com.smsc.headend.task.engine.service.push.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.SystemClock;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.smsc.headend.common.enums.DeviceType;
import com.smsc.headend.module.asset.entity.Dcu;
import com.smsc.headend.module.asset.entity.DeviceRelation;
import com.smsc.headend.module.asset.entity.Meter;
import com.smsc.headend.module.asset.entity.UnRegisterDevice;
import com.smsc.headend.module.protocol.cosem.bo.CosemData;
import com.smsc.headend.module.push.entity.ConfigNotifyType;
import com.smsc.headend.module.push.entity.ConfigNotifyTypeParam;
import com.smsc.headend.module.report.entity.LogUnRegisterMeterHis;
import com.smsc.headend.module.task.dto.Task;
import com.smsc.headend.task.engine.mapper.DeviceRelationMapper;
import com.smsc.headend.task.engine.po.DeviceDetail;
import com.smsc.headend.task.engine.service.CosemXmlService;
import com.smsc.headend.task.engine.service.FeignAssetManagementService;
import com.smsc.headend.task.engine.service.push.ConfigNotifyTypeParamService;
import com.smsc.headend.task.engine.service.push.DcuRegisterTaskService;
import com.smsc.headend.task.engine.service.push.PushDataDecodeHandler;
import com.smsc.headend.task.engine.service.task.TaskService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;


/**
 * (业务实现)
 *
 * @author wjh
 * @date 2022/6/17 9:10.
 */
@Slf4j
public class DcuRegistryHandlerImpl implements PushDataDecodeHandler {

    @Autowired
    ConfigNotifyTypeParamService paramService;
    @Autowired
    CosemXmlService cosemXmlService;
    @Autowired
    FeignAssetManagementService assetManagementService;
    @Autowired
    TaskService taskService;
    @Autowired
    DcuRegisterTaskService registerTaskService;
    @Autowired
    DeviceRelationMapper deviceRelationMapper;

    @Override
    public void handler(CosemData cosemData, ConfigNotifyType configNotifyType, DeviceDetail deviceDetail, Long dataItemId, Date uploadDate) {
        Long deviceId = deviceDetail.getDeviceId();
        List<ConfigNotifyTypeParam> params = paramService.getListByNotifyTypeId(configNotifyType.getNotifyTypeId());
        Map<String, ConfigNotifyTypeParam> paramMap = params
                .stream()
                .collect(Collectors.toMap(ConfigNotifyTypeParam::getParamName, e -> e));
        ConfigNotifyTypeParam childDataParam = paramMap.get("childData");
        ConfigNotifyTypeParam deviceSnParam = paramMap.get("deviceSn");
        ConfigNotifyTypeParam eui64Param = paramMap.get("eui64");
        ConfigNotifyTypeParam statusParam = paramMap.get("status");
        ConfigNotifyTypeParam dateParam = paramMap.get("date");
        if (null == childDataParam) {
            log.error("decode dcu registry error,no param:childDataParam");
            return;
        }
        if (null == eui64Param) {
            log.error("decode dcu registry error,no param:eui64Param");
            return;
        }
        if (null == statusParam) {
            log.error("decode dcu registry error,no param:statusParam");
            return;
        }
        if (null == dateParam) {
            log.error("decode dcu registry error,no param:dateParam");
            return;
        }
        if(null == deviceSnParam) {
            log.error("decode dcu registry error,no param:deviceSnParam");
            return;
        }
        CosemData cosemDataParent = paramService.getCosemDataByIndex(cosemData, childDataParam.getParamIndex());
        if (null == cosemDataParent) {
            log.error("decode dcu registry error,cosemDataParent is null");
            return;
        }
        List<CosemData> childData = cosemDataParent.getChild();
        if (CollUtil.isNotEmpty(childData)) {
            for (CosemData child : childData) {
                // 该deviceSn为表计资产号
                CosemData deviceSnCosemData = paramService.getCosemDataByIndex(child, deviceSnParam.getParamIndex());
                CosemData eui64CosemData = paramService.getCosemDataByIndex(child, eui64Param.getParamIndex());
                CosemData statusCosemData = paramService.getCosemDataByIndex(child, statusParam.getParamIndex());
                CosemData dateCosemData = paramService.getCosemDataByIndex(child, dateParam.getParamIndex());
                String deviceSn = null == deviceSnCosemData ? null : Convert.toStr(cosemXmlService.getData(deviceSnCosemData.getType(), deviceSnCosemData.getValue()));
                String eui64 = null == deviceSnCosemData ? null : Convert.toStr(cosemXmlService.getData(eui64CosemData.getType(), eui64CosemData.getValue()));
                Integer status = null == deviceSnCosemData ? null : Convert.toInt(cosemXmlService.getData(statusCosemData.getType(), statusCosemData.getValue()));
                Date date = null == deviceSnCosemData ? null : (Date) (cosemXmlService.getData(dateCosemData.getType(), dateCosemData.getValue()));
                log.info("the register notification info: deviceSn {}, eui64 {} status {}, date {}", deviceSn, eui64, status, date.getTime() / 1000);

                // Status 1: New meter; 2: Registered and online meter; 3: Registered and offline meter; 4: Rejected meter (只需处理New Meter的)
                if (status == 1) {
                    UnRegisterDevice unRegisterDevice = assetManagementService.getUnRegisterRecordByDeviceSn(deviceSn);
                    String autoRegisterSwitch = assetManagementService.getPropertyValue("AUTO_REGISTER_SWITCH");
                    if (!Convert.toBool(autoRegisterSwitch, false)) {
                        log.info("the GUI auto register switch is disabled, no device task will generate");
                    } else {
                        if (null == unRegisterDevice) {
                            //record 没记录马上生成任务
                            handleRegisterTask(deviceSn, deviceId);
                        } else if (SystemClock.now() / 1000 - unRegisterDevice.getTvUpdate() > 300) {
                            // 大于5分钟产生task 防止短时间内dcu重复上报生成task任务
                            handleRegisterTask(deviceSn, deviceId);
                        }
                    }
                    // 任务不产生也要插入未注册的设备记录
                    assetManagementService.saveUnRegisterRecord(deviceSn, deviceId, SystemClock.now() / 1000, "Push Notify");
                } else {
                    log.info("The status of the meter {} is not new meter, no action need to do", deviceSn);
                }
            }
        } else {
            log.error("incorrect register notification {}", childData);
        }

    }

    private void handleRegisterTask(String meterId, Long deviceId) {
        Meter meter = assetManagementService.getMeterByMfgSn(meterId);
        if (null == meter) {
            log.info("the meter not exist in udis {}", meterId);
        } else {
            DeviceRelation deviceRelation = deviceRelationMapper.selectOne(Wrappers.<DeviceRelation>lambdaQuery()
                    .eq(DeviceRelation::getDeviceId, meter.getMeterId()).eq(DeviceRelation::getDeviceType, DeviceType.METER.getId()));
            if (null != deviceRelation) {
                // 1. 电表在该DCU下有挂接关系 如果已经是mounted状态不做任何操作,否则需要下发白名单到dcu上面
                // 2. 电表在该DCU下无挂接关系 认为是漂移到其他DCU上 或者是未挂接到任何DCU上.如果是未挂接到任何集中器上,则下发白名单. 如果是漂移到其他集中器，则需删除之前集中器上的白名单,解除meter relation关系。并下发新的白名单到当前DCU上面并建立新的meter relation关系
                if (deviceRelation.getParentId().equals(deviceId)) {
                    if (meter.getMountStatus() != null && meter.getMountStatus() == Meter.MeterMountStatus.Mounted.getCode()) {
                        log.info("the meter {} mounted to the dcu {}, no action need to do", meterId, deviceId);
                    } else {
                        insertSetWhiteListTask(deviceRelation.getParentId(), meter);
                        log.info("the meter {} not mounted to the dcu {}, insert setWhiteList task", meterId, deviceId);
                    }
                } else {
                    // 2-1 找出该meter是否跟旧的dcu有关系,有的话旧的dcu删除白名单
                    insertDeleteWhiteListTask(deviceRelation.getParentId(), meter);

                    // 2-2 下发白名单到新的dcu上
                    insertSetWhiteListTask(deviceId, meter);
                }
            } else {
                // 3: 表计跟dcu无任何关系
                // 3-1: 下发白名单到当前dcu上面, 成功后在handler里面挂接dcu与meter之间的关系
                log.error("the meter have no meter relation {}", meterId);
                insertSetWhiteListTask(deviceId, meter);
            }
        }
    }

    private void insertSetWhiteListTask(Long deviceId, Meter meter) {
        Dcu dcu = assetManagementService.getDcuById(deviceId);
        if (dcu != null) {
            Task task = registerTaskService.dcuSetMeterListTask(meter, dcu.getDcuId());
            log.info("insert register setMeterList task {}", JSONUtil.toJsonStr(task));
            assetManagementService.saveUnregisterMeterHis(meter.getMeterId(), dcu.getDcuId(), task.getTaskNo(), LogUnRegisterMeterHis.ActionType.SetMeterList.getCode(),
                    LogUnRegisterMeterHis.UnRegisterSrc.Auto.getCode(), LogUnRegisterMeterHis.UnRegisterStatus.PROCESSING.getCode(), SystemClock.now() / 1000, SystemClock.now() / 1000, "");
            taskService.sendTaskToGenerator(task);
        } else {
            log.info("the current dcu not exist {}", deviceId);
        }
    }

    private void insertDeleteWhiteListTask(Long deviceId, Meter meter) {
        Dcu oldDcu = assetManagementService.getDcuById(deviceId);
        if (oldDcu != null) {
            Task task = registerTaskService.dcuDeleteMeterListTask(meter, oldDcu.getDcuId());
            log.info("insert register deleteMeterList task {}", JSONUtil.toJsonStr(task));
            assetManagementService.saveUnregisterMeterHis(meter.getMeterId(), oldDcu.getDcuId(), task.getTaskNo(), LogUnRegisterMeterHis.ActionType.DeleteMeterList.getCode(),
                    LogUnRegisterMeterHis.UnRegisterSrc.Auto.getCode(), LogUnRegisterMeterHis.UnRegisterStatus.PROCESSING.getCode(), SystemClock.now() / 1000, SystemClock.now() / 1000, "");
            taskService.sendTaskToGenerator(task);
        } else {
            log.info("the old dcu not exist {}", deviceId);
        }
    }
}
